package cn.bytengine.stem.functional.invoker;

import cn.bytengine.stem.functional.Consumers;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

public class ConsumerInvokerFactory implements InvokerFactory {
    private static final String CHECK_CONSUMERS_PREFIX_FLAG = Consumers.class.getName().replaceAll("\\.", "\\/") + '$';
    private static final Map<String, Function<Serializable, Invoker>> CONSUMER_INVOKER_MAPPING = new ConcurrentHashMap<>();

    static {
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer0.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer0Invoker((Consumers.Consumer0) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer1.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer1Invoker((Consumers.Consumer1) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer2.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer2Invoker((Consumers.Consumer2) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer3.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer3Invoker((Consumers.Consumer3) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer4.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer4Invoker((Consumers.Consumer4) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer5.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer5Invoker((Consumers.Consumer5) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer6.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer6Invoker((Consumers.Consumer6) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer7.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer7Invoker((Consumers.Consumer7) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer8.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer8Invoker((Consumers.Consumer8) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.Consumer9.class.getName().replaceAll("\\.", "\\/"), serializable -> consumer9Invoker((Consumers.Consumer9) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.ConsumerA.class.getName().replaceAll("\\.", "\\/"), serializable -> consumerAInvoker((Consumers.ConsumerA) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.ConsumerB.class.getName().replaceAll("\\.", "\\/"), serializable -> consumerBInvoker((Consumers.ConsumerB) serializable));
        CONSUMER_INVOKER_MAPPING.put(Consumers.ConsumerC.class.getName().replaceAll("\\.", "\\/"), serializable -> consumerCInvoker((Consumers.ConsumerC) serializable));
    }

    public static Invoker consumer0Invoker(Consumers.Consumer0 consumer) {
        return args -> consumer.accept();
    }

    public static <T1> Invoker consumer1Invoker(Consumers.Consumer1<T1> consumer) {
        return args -> consumer.accept((T1) args[0]);
    }

    public static <T1, T2> Invoker consumer2Invoker(Consumers.Consumer2<T1, T2> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1]);
    }

    public static <T1, T2, T3> Invoker consumer3Invoker(Consumers.Consumer3<T1, T2, T3> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2]);
    }

    public static <T1, T2, T3, T4> Invoker consumer4Invoker(Consumers.Consumer4<T1, T2, T3, T4> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3]);
    }

    public static <T1, T2, T3, T4, T5> Invoker consumer5Invoker(Consumers.Consumer5<T1, T2, T3, T4, T5> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3], (T5) args[4]);
    }

    public static <T1, T2, T3, T4, T5, T6> Invoker consumer6Invoker(Consumers.Consumer6<T1, T2, T3, T4, T5, T6> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3], (T5) args[4], (T6) args[5]);
    }

    public static <T1, T2, T3, T4, T5, T6, T7> Invoker consumer7Invoker(Consumers.Consumer7<T1, T2, T3, T4, T5, T6, T7> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3], (T5) args[4], (T6) args[5], (T7) args[6]);
    }

    public static <T1, T2, T3, T4, T5, T6, T7, T8> Invoker consumer8Invoker(Consumers.Consumer8<T1, T2, T3, T4, T5, T6, T7, T8> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3], (T5) args[4], (T6) args[5], (T7) args[6], (T8) args[7]);
    }

    public static <T1, T2, T3, T4, T5, T6, T7, T8, T9> Invoker consumer9Invoker(Consumers.Consumer9<T1, T2, T3, T4, T5, T6, T7, T8, T9> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3], (T5) args[4], (T6) args[5], (T7) args[6], (T8) args[7], (T9) args[8]);
    }

    public static <T1, T2, T3, T4, T5, T6, T7, T8, T9, TA> Invoker consumerAInvoker(Consumers.ConsumerA<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3], (T5) args[4], (T6) args[5], (T7) args[6], (T8) args[7], (T9) args[8], (TA) args[9]);
    }

    public static <T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB> Invoker consumerBInvoker(Consumers.ConsumerB<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3], (T5) args[4], (T6) args[5], (T7) args[6], (T8) args[7], (T9) args[8], (TA) args[9], (TB) args[10]);
    }

    public static <T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC> Invoker consumerCInvoker(Consumers.ConsumerC<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC> consumer) {
        return args -> consumer.accept((T1) args[0], (T2) args[1], (T3) args[2], (T4) args[3], (T5) args[4], (T6) args[5], (T7) args[6], (T8) args[7], (T9) args[8], (TA) args[9], (TB) args[11], (TC) args[12]);
    }

    public Invoker create(Serializable serializable) {
        SerializedLambda serializedLambda = MethodHandles.getSerializedLambda(serializable);
        String targetFunctionalClassName = serializedLambda.getFunctionalInterfaceClass();
        if (targetFunctionalClassName.startsWith(CHECK_CONSUMERS_PREFIX_FLAG)) {
            return CONSUMER_INVOKER_MAPPING.get(targetFunctionalClassName).apply(serializable);
        }
        throw new UnsupportedTypeException();
    }
}
